﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Media;
using System.IO;
using LCARSLibrary.Properties;

namespace LCARSLibrary.Controls
{
    public class LCARSBase : Control
    {
        #region fields

        ControlGroup controlGroup;
        Color textColor;
        Color color;
        Color? tmpColor;
        Color flashColor1;
        Color flashColor2;
        int flashOrderIndex;
        bool flashTextColor;

        #region static members

        static Dictionary<LCARSBase, ControlGroup> controlGroups;
        static string languageID;
        static Timer timer;
        static Dictionary<int, List<LCARSBase>> flashCtrls;
        static int currentFlashOrderIndex;
        static int maxFlashOrderIndex;
        static SoundPlayer soundPlayer;
        static bool exclusiveLoopPlaying = false;

        #endregion

        #endregion

        #region properties

        [Browsable(true), DefaultValue(typeof(ControlGroup), "None"), Category("LCARS")]
        public ControlGroup ControlGroup
        {
            get { return controlGroup; }
            set { controlGroup = value; SetControlGroup(controlGroup); }
        }

        [Browsable(true), DefaultValue(typeof(PredefinedColor), "Default1"), Category("LCARS")]
        public PredefinedColor PredefinedColor
        {
            get { return GetPredefinedColor(this.color); }
            set { this.Color = GetColor(value); }
        }

        [Browsable(true), DefaultValue(typeof(Color), "#666666"), Category("LCARS"), RefreshProperties(System.ComponentModel.RefreshProperties.All)]
        public Color Color
        {
            get { return this.color; }
            set { this.color = value; base.Invalidate(); }
        }

        [Browsable(true), DefaultValue(typeof(Color), "Black"), Category("LCARS")]
        public virtual Color TextColor
        {
            get { return this.textColor; }
            set { this.textColor = value; base.Invalidate(); }
        }

        [Browsable(true), DefaultValue(typeof(Padding), "2, 2, 2, 2")]
        public new Padding Margin
        {
            get { return base.Margin; }
            set { base.Margin = value; }
        }

        [Browsable(false)]
        protected virtual Color SecondaryColor
        {
            get { return ControlPaint.Light(this.Color); }
        }

        [Browsable(true), DefaultValue(null), Category("LCARS")]
        public string LanguageEntryID { get; set; }

        [Browsable(false)]
        public override Color ForeColor
        {
            get { return base.ForeColor; }
            set { base.ForeColor = value; }
        }

        [Browsable(false)]
        public override Color BackColor
        {
            get { return base.BackColor; }
            set { base.BackColor = value; }
        }

        #region static members

        [Browsable(false)]
        public static int FlashInterval
        {
            get
            {
                return timer.Interval;
            }
            set
            {
                if (timer != null)
                    timer.Interval = value;
            }
        }

        [Browsable(false)]
        public static string LanguageID { get { return languageID; } }

        public static bool ExclusiveLoopPlaying
        {
            get { return exclusiveLoopPlaying; }
            set { exclusiveLoopPlaying = value; }
        }

        #endregion

        #endregion

        #region events

        [Browsable(true), Category("LCARS")]
        public event EventHandler<LanguageEventArgs> LanguageChanged;

        protected virtual void OnLanguageChanged(object sender, LanguageEventArgs e)
        {
            EventHandler<LanguageEventArgs> temp = LanguageChanged;
            if (temp != null)
            {
                temp(sender, e);
            }
        }

        #endregion

        public LCARSBase()
        {
            color = Colors.Default1;
            tmpColor = null;

            FlashInterval = 470;
            currentFlashOrderIndex = 0;

            if (controlGroups == null)
                controlGroups = new Dictionary<LCARSBase, ControlGroup>();

            controlGroups.Add(this, ControlGroup.None);

            if (flashCtrls == null)
                flashCtrls = new Dictionary<int, List<LCARSBase>>();

            if (timer == null)
            {
                timer = new Timer();
                timer.Tick += new EventHandler(timer_Tick);
                timer.Interval = FlashInterval;
            }
        }

        public void Flash(bool flash)
        {
            Flash(flash, this.Color, this.SecondaryColor, 0);
        }

        public void Flash(bool flash, int orderIndex)
        {
            Flash(flash, this.Color, this.SecondaryColor, orderIndex);
        }

        public void Flash(bool flash, Color color2)
        {
            Flash(flash, this.Color, color2, 0);
        }

        public void Flash(bool flash, Color color2, int orderIndex)
        {
            Flash(flash, this.Color, color2, orderIndex);
        }

        public void Flash(bool flash, Color color1, Color color2)
        {
            Flash(flash, color1, color2, 0);
        }

        public void Flash(bool flash, Color color1, Color color2, int orderIndex)
        {
            Flash(flash, color1, color2, orderIndex, false);
        }

        public void Flash(bool flash, Color color1, Color color2, int orderIndex, bool flashTextColor)
        {
            if (flash && flashCtrls.Where((kv) => { return kv.Value.Contains(this); }).Count() > 0)
                return;

            if (flash)
            {
                this.flashOrderIndex = orderIndex;
                this.flashTextColor = flashTextColor;

                if (maxFlashOrderIndex < orderIndex)
                    maxFlashOrderIndex = orderIndex;

                this.flashColor1 = color1;
                this.flashColor2 = color2;

                if (!flashTextColor)
                {
                    tmpColor = this.Color;
                    this.Color = flashColor1;
                }
                else
                {
                    tmpColor = this.TextColor;
                    this.TextColor = flashColor1;
                }

                if (!flashCtrls.ContainsKey(orderIndex))
                    flashCtrls.Add(orderIndex, new List<LCARSBase>());

                flashCtrls[orderIndex].Add(this);

                if (!timer.Enabled)
                {
                    currentFlashOrderIndex = 0;
                    timer.Start();
                }
            }
            else
            {
                if (tmpColor.HasValue)
                {
                    if (!flashTextColor)
                        this.Color = tmpColor.Value;
                    else
                        this.TextColor = tmpColor.Value;
                }

                if (flashCtrls.ContainsKey(orderIndex))
                {
                    flashCtrls[orderIndex].Remove(this);

                    if (flashCtrls[orderIndex].Count == 0)
                        flashCtrls.Remove(orderIndex);
                }

                if (flashCtrls.Count > 0)
                    maxFlashOrderIndex = flashCtrls.Keys.Max();
                else
                    maxFlashOrderIndex = 0;

                if (flashCtrls.Count == 0)
                    timer.Stop();
            }
        }


        private Color GetColor(PredefinedColor predefinedColor)
        {
            switch (predefinedColor)
            {
                case PredefinedColor.Default1:
                    return Colors.Default1;
                case PredefinedColor.Default2:
                    return Colors.Default2;
                case PredefinedColor.Default3:
                    return Colors.Default3;
                case PredefinedColor.Blue1:
                    return Colors.Blue1;
                case PredefinedColor.Blue2:
                    return Colors.Blue2;
                case PredefinedColor.Blue3:
                    return Colors.Blue3;
                case PredefinedColor.Blue4:
                    return Colors.Blue4;
                case PredefinedColor.Orange1:
                    return Colors.Orange1;
                case PredefinedColor.Orange2:
                    return Colors.Orange2;
                case PredefinedColor.Orange3:
                    return Colors.Orange3;
                case PredefinedColor.Orange4:
                    return Colors.Orange4;
                case PredefinedColor.Orange5:
                    return Colors.Orange5;
                case PredefinedColor.Red1:
                    return Colors.Red1;
                case PredefinedColor.Red2:
                    return Colors.Red2;
                case PredefinedColor.Red3:
                    return Colors.Red3;
                default:
                    return Color.Empty;
            }

            
        }

        private PredefinedColor GetPredefinedColor(Color color)
        {
            if (color == Colors.Default1)
                return PredefinedColor.Default1;
            else if (color == Colors.Default2)
                return PredefinedColor.Default2;
            else if (color == Colors.Default3)
                return PredefinedColor.Default3;

            else if (color == Colors.Blue1)
                return PredefinedColor.Blue1;
            else if (color == Colors.Blue2)
                return PredefinedColor.Blue2;
            else if (color == Colors.Blue3)
                return PredefinedColor.Blue3;
            else if (color == Colors.Blue4)
                return PredefinedColor.Blue4;

            else if (color == Colors.Orange1)
                return PredefinedColor.Orange1;
            else if (color == Colors.Orange2)
                return PredefinedColor.Orange2;
            else if (color == Colors.Orange3)
                return PredefinedColor.Orange3;
            else if (color == Colors.Orange4)
                return PredefinedColor.Orange4;
            else if (color == Colors.Orange5)
                return PredefinedColor.Orange5;

            else if (color == Colors.Red1)
                return PredefinedColor.Red1;
            else if (color == Colors.Red2)
                return PredefinedColor.Red2;
            else if (color == Colors.Red3)
                return PredefinedColor.Red3;

            else
                return PredefinedColor.None;
        }

        private void SetControlGroup(ControlGroup groups)
        {
            controlGroups[this] = groups;
        }

        protected override void Dispose(bool disposing)
        {
            controlGroups.Remove(this);

            base.Dispose(disposing);
        }


        #region static members

        public static List<LCARSBase> GetGroupControls(ControlGroup cg)
        {
            var result = from x in controlGroups
                         where x.Value.HasFlag(cg)
                         select x.Key;

            return result.ToList();
        }

        public static void SetLanguage(string languageID)
        {
            if (String.IsNullOrEmpty(languageID))
                return;

            if (LCARSBase.LanguageID == languageID)
                return;

            LCARSBase.languageID = languageID;

            if (controlGroups != null)
            {
                foreach (var item in controlGroups.Keys)
                {
                    item.OnLanguageChanged(item, new LanguageEventArgs() { LanguageID = LCARSBase.languageID });
                }
            }
        }


        public static void PlaySoundEffectSync(SoundEffect soundEffect)
        {
            if (soundEffect != SoundEffect.None)
            {
                switch (soundEffect)
                {
                    case SoundEffect.Beep1:
                        PlaySoundSync(Resources.Beep1);
                        break;
                    case SoundEffect.Beep2:
                        PlaySoundSync(Resources.Beep2);
                        break;
                    case SoundEffect.Beep3:
                        PlaySoundSync(Resources.Beep3);
                        break;
                    case SoundEffect.Beep4:
                        PlaySoundSync(Resources.Beep4);
                        break;
                    case SoundEffect.Beep5:
                        PlaySoundSync(Resources.Beep5);
                        break;
                    case SoundEffect.Beep6:
                        PlaySoundSync(Resources.Beep6);
                        break;
                    case SoundEffect.Beep7:
                        PlaySoundSync(Resources.Beep7);
                        break;
                    case SoundEffect.Disabled:
                        PlaySoundSync(Resources.Disabled);
                        break;
                    case SoundEffect.Error1:
                        PlaySoundSync(Resources.Error1);
                        break;
                    case SoundEffect.Error2:
                        PlaySoundSync(Resources.Error2);
                        break;
                    case SoundEffect.Error3:
                        PlaySoundSync(Resources.Error3);
                        break;
                    case SoundEffect.Message1:
                        PlaySoundSync(Resources.Message1);
                        break;
                    case SoundEffect.Message2:
                        PlaySoundSync(Resources.Message2);
                        break;
                    case SoundEffect.Message3:
                        PlaySoundSync(Resources.Message3);
                        break;
                    case SoundEffect.Message4:
                        PlaySoundSync(Resources.Message4);
                        break;
                    case SoundEffect.Message5:
                        PlaySoundSync(Resources.Message5);
                        break;
                    case SoundEffect.Message6:
                        PlaySoundSync(Resources.Message6);
                        break;
                    case SoundEffect.Question:
                        PlaySoundSync(Resources.Question);
                        break;
                    case SoundEffect.Yes1:
                        PlaySoundSync(Resources.Yes1);
                        break;
                    case SoundEffect.Yes2:
                        PlaySoundSync(Resources.Yes2);
                        break;
                    case SoundEffect.No1:
                        PlaySoundSync(Resources.No1);
                        break;
                    case SoundEffect.No2:
                        PlaySoundSync(Resources.No2);
                        break;
                    case SoundEffect.ViewOff:
                        PlaySoundSync(Resources.ViewOff);
                        break;
                    case SoundEffect.ViewOn:
                        PlaySoundSync(Resources.ViewOn);
                        break;
                    case SoundEffect.RedAlert:
                        PlaySoundSync(Resources.RedAlert);
                        break;
                    case SoundEffect.BlueAlert:
                        PlaySoundSync(Resources.BlueAlert);
                        break;
                }
            }
        }


        public static void PlaySoundEffect(SoundEffect soundEffect)
        {
            PlaySoundEffect(soundEffect, false);
        }

        public static void PlaySoundEffect(SoundEffect soundEffect, bool looping)
        {
            if (soundEffect != SoundEffect.None)
            {
                switch (soundEffect)
                {
                    case SoundEffect.Beep1:
                        PlaySound(Resources.Beep1, looping);
                        break;
                    case SoundEffect.Beep2:
                        PlaySound(Resources.Beep2, looping);
                        break;
                    case SoundEffect.Beep3:
                        PlaySound(Resources.Beep3, looping);
                        break;
                    case SoundEffect.Beep4:
                        PlaySound(Resources.Beep4, looping);
                        break;
                    case SoundEffect.Beep5:
                        PlaySound(Resources.Beep5, looping);
                        break;
                    case SoundEffect.Beep6:
                        PlaySound(Resources.Beep6, looping);
                        break;
                    case SoundEffect.Beep7:
                        PlaySound(Resources.Beep7, looping);
                        break;
                    case SoundEffect.Disabled:
                        PlaySound(Resources.Disabled, looping);
                        break;
                    case SoundEffect.Error1:
                        PlaySound(Resources.Error1, looping);
                        break;
                    case SoundEffect.Error2:
                        PlaySound(Resources.Error2, looping);
                        break;
                    case SoundEffect.Error3:
                        PlaySound(Resources.Error3, looping);
                        break;
                    case SoundEffect.Message1:
                        PlaySound(Resources.Message1, looping);
                        break;
                    case SoundEffect.Message2:
                        PlaySound(Resources.Message2, looping);
                        break;
                    case SoundEffect.Message3:
                        PlaySound(Resources.Message3, looping);
                        break;
                    case SoundEffect.Message4:
                        PlaySound(Resources.Message4, looping);
                        break;
                    case SoundEffect.Message5:
                        PlaySound(Resources.Message5, looping);
                        break;
                    case SoundEffect.Message6:
                        PlaySound(Resources.Message6, looping);
                        break;
                    case SoundEffect.Question:
                        PlaySound(Resources.Question, looping);
                        break;
                    case SoundEffect.Yes1:
                        PlaySound(Resources.Yes1, looping);
                        break;
                    case SoundEffect.Yes2:
                        PlaySound(Resources.Yes2, looping);
                        break;
                    case SoundEffect.No1:
                        PlaySound(Resources.No1, looping);
                        break;
                    case SoundEffect.No2:
                        PlaySound(Resources.No2, looping);
                        break;
                    case SoundEffect.ViewOff:
                        PlaySound(Resources.ViewOff, looping);
                        break;
                    case SoundEffect.ViewOn:
                        PlaySound(Resources.ViewOn, looping);
                        break;
                    case SoundEffect.RedAlert:
                        PlaySound(Resources.RedAlert, looping);
                        break;
                    case SoundEffect.BlueAlert:
                        PlaySound(Resources.BlueAlert, looping);
                        break;
                }
            }
        }


        public static void PlaySoundSync(string fileName)
        {
            InitializeSoundPlayer();
            soundPlayer.Stop();

            using (SoundPlayer playerSync = new SoundPlayer(fileName))
                playerSync.PlaySync();
        }

        public static void PlaySoundSync(Stream stream)
        {
            InitializeSoundPlayer();
            soundPlayer.Stop();

            using (SoundPlayer playerSync = new SoundPlayer(stream))
                playerSync.PlaySync();
        }


        public static void PlaySound(string fileName)
        {
            PlaySound(fileName, false);
        }

        public static void PlaySound(string fileName, bool looping)
        {
            InitializeSoundPlayer();

            if (ExclusiveLoopPlaying && soundPlayer.Tag != null && (bool)soundPlayer.Tag)
                return;

            soundPlayer.Stop();
            soundPlayer.SoundLocation = fileName;
            soundPlayer.Tag = looping;
            soundPlayer.LoadAsync();
        }

        public static void PlaySound(Stream stream)
        {
            PlaySound(stream, false);
        }

        public static void PlaySound(Stream stream, bool looping)
        {
            InitializeSoundPlayer();

            if (ExclusiveLoopPlaying && soundPlayer.Tag != null && (bool)soundPlayer.Tag)
                return;

            soundPlayer.Stop();
            soundPlayer.Stream = stream;
            soundPlayer.Tag = looping;
            soundPlayer.LoadAsync();
        }


        public static void StopPlaying()
        {
            InitializeSoundPlayer();

            soundPlayer.Stop();
        }


        private static void InitializeSoundPlayer()
        {
            if (soundPlayer == null)
            {
                soundPlayer = new SoundPlayer();
                soundPlayer.LoadCompleted += soundPlayer_LoadCompleted;
            }
        }

        private static int MathModulo(int value, int mod)
        {
            return (Math.Abs(value * mod) + value) % mod;
        }


        private static void timer_Tick(object sender, EventArgs e)
        {
            int previousFlashOrderIndex = MathModulo(currentFlashOrderIndex - 1, maxFlashOrderIndex + 2);

            if (flashCtrls.ContainsKey(previousFlashOrderIndex))
            {
                foreach (var item in flashCtrls[previousFlashOrderIndex])
                {
                    if (!item.flashTextColor)
                        item.Color = item.flashColor1;
                    else
                        item.TextColor = item.flashColor1;
                }
            }

            if (flashCtrls.ContainsKey(currentFlashOrderIndex))
            {
                foreach (var item in flashCtrls[currentFlashOrderIndex])
                {
                    if (!item.flashTextColor)
                        item.Color = item.flashColor2;
                    else
                        item.TextColor = item.flashColor2;
                }
            }

            currentFlashOrderIndex = MathModulo(currentFlashOrderIndex + 1, maxFlashOrderIndex + 2);
        }

        private static void soundPlayer_LoadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if ((bool)soundPlayer.Tag)
                soundPlayer.PlayLooping();
            else
                soundPlayer.Play();
        }

        #endregion
    }
}
